import _ from 'lodash'

export class EventBus {
  constructor() {
    this.listeners = new Map()
    this.wildcardListeners = new Map()
  }
  
  on(event, handler) {
    if (event.includes('*')) {
      if (!this.wildcardListeners.has(event)) {
        this.wildcardListeners.set(event, new Set())
      }
      this.wildcardListeners.get(event).add(handler)
    } else {
      if (!this.listeners.has(event)) {
        this.listeners.set(event, new Set())
      }
      this.listeners.get(event).add(handler)
    }
  }
  
  off(event, handler) {
    if (event.includes('*')) {
      const handlers = this.wildcardListeners.get(event)
      if (handlers) {
        handlers.delete(handler)
        if (handlers.size === 0) {
          this.wildcardListeners.delete(event)
        }
      }
    } else {
      const handlers = this.listeners.get(event)
      if (handlers) {
        handlers.delete(handler)
        if (handlers.size === 0) {
          this.listeners.delete(event)
        }
      }
    }
  }
  
  emit(event, data) {
    if (this.listeners.has(event)) {
      this.listeners.get(event).forEach(handler => {
        try {
          handler(data)
        } catch (err) {
          console.error(`Event handler error [${event}]:`, err)
        }
      })
    }
    
    this.wildcardListeners.forEach((handlers, pattern) => {
      if (this.matchPattern(event, pattern)) {
        handlers.forEach(handler => {
          try {
            handler(event, data)
          } catch (err) {
            console.error(`Wildcard handler error [${pattern}]:`, err)
          }
        })
      }
    })
  }
  
  async emitAsync(event, data) {
    const promises = []
    
    if (this.listeners.has(event)) {
      this.listeners.get(event).forEach(handler => {
        promises.push(Promise.resolve(handler(data)).catch(err => {
          console.error(`Event handler error [${event}]:`, err)
        }))
      })
    }
    
    this.wildcardListeners.forEach((handlers, pattern) => {
      if (this.matchPattern(event, pattern)) {
        handlers.forEach(handler => {
          promises.push(Promise.resolve(handler(event, data)).catch(err => {
            console.error(`Wildcard handler error [${pattern}]:`, err)
          }))
        })
      }
    })
    
    await Promise.all(promises)
  }
  
  matchPattern(event, pattern) {
    const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$')
    return regex.test(event)
  }
  
  once(event, handler) {
    const wrapper = (data) => {
      handler(data)
      this.off(event, wrapper)
    }
    this.on(event, wrapper)
  }
  
  removeAllListeners(event) {
    if (event) {
      this.listeners.delete(event)
      this.wildcardListeners.delete(event)
    } else {
      this.listeners.clear()
      this.wildcardListeners.clear()
    }
  }
}